home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
msdos
/
raytrace
/
pov
/
gen
/
plant05
/
plant050.cpp
< prev
Wrap
C/C++ Source or Header
|
1993-10-07
|
28KB
|
631 lines
/*****************************************************************************
Program : PLANT.EXE v0.50a
Purpose : Create plant-like objects for PoV Ray v1.0 and v2.0 and Polyray v1.6
raytracers and also export to CTDS format.
Created : 9/28/93
By : Rob Bryerton CIS [73747,433]
File : PLANT050.CPP
Compiler: Microsoft C++ v8.00
Model : Small
Comments: Creates 'organic' plant-like objects using spheres...
*****************************************************************************/
#include <graph.h> // for graphics functions
#include <stdlib.h> // for srand(), rand()
#include <time.h> // for randomize()
#include <math.h> // for sin(), cos()
#include <conio.h> // for getch()
#include <fstream.h> // for disk access, cin, cout, etc
#include <string.h> // for strcpy()
#include <float.h> // for FLT_MIN, FLT_MAX, etc
#define VERSION "0.50a"
#define GREEN 2 // color indices
#define RED 4
#define LIGHTGREEN 10
#define YELLOW 14
enum bool {FALSE, TRUE};
enum Format {POV2,POV1,POLY,CTDS};
struct Vector{ double x,y,z;};
void calc_bounds(void);
void calc_branch(int size, double theta, double x, double y);
void calc_cluster(int size, double x, double y);
void close_display(void);
void end_run(void);
void get_inputs(void);
void init_display(int v_mode);
void new_union(void);
void process_args (int argc, char* argv[]);
void process_option (char* s);
void reset_bounds(void);
void set_precision(char* prec);
void show_title(void);
void usage(void);
void write_header(void);
void write_piece(void);
void write_union_end(void);
void write_end(void);
Format format = POV2; // default output
bool spheres_only = FALSE; // write spheres and cones
bool nest_bounds = FALSE;
bool bound_sum = FALSE; // include bound stats w/ each bound written
bool display = FALSE; // ****** change to FALSE for release version
bool extended = FALSE; // ****** change to FALSE for release version
bool write_texture=TRUE; // write texture on EACH segment...choose from 4 colors
int v_mode=0; // ****** change to 0 for release version
long bound_val=30,bound_count=0;// bnd every ...and count # bounds written for next nested
long total_bounds = 1; // start w/1 for default global bounding box
long counter=0; // total # of objects written
char com[3] = "//"; // default comment char
char filename[80]; // outfile name
char union_name[80]; // for output file union name
char format_name[20]; // for FORMAT statement in output file
char buff[257]; // general use buffer
char texture[80];
char prev_texture[80]; // the one b/4 texture !
clock_t start, finish; // ******** 2 lines for timer
double duration;
double basic_rad = 1.0,sphere_rad=1.0; // basic radius and current sphere radius
double bound_radi = 0.0; // current max sph. radius for bound info
double prev_sphere_rad,max_radius; // radii
double ht_scale = 1.0; // flat or tall plant
double internal_scale, user_scale, overall_scale; // fixes numbers so object is 10 units
Vector ray = {0.0, 0.0, 0.0}; // outfile vector
Vector prev_ray = {0.0, 0.0, 0.0};
Vector min = {FLT_MAX, FLT_MAX, FLT_MAX}; // for nested bounding info
Vector max = {FLT_MIN, FLT_MIN, FLT_MIN};
Vector gmin = {FLT_MAX, FLT_MAX, FLT_MAX}; // for scene extent and
Vector gmax = {FLT_MIN, FLT_MIN, FLT_MIN}; // global bounding info
ofstream outfile; // these vars are used for basic shape calculations
double pi=3.1415926535897932384626; // pi ...lower #'s change 'radius' of plant
int nume=30; // numerator of probability ...left or rite ?
int denom=100; // denominator of prob.
int number=4; // number of branches per cluster
double rad=4.5; // length of straight line segments
double deltheta=0.1; // change in THETA (radians)
int segs=60; // max objects & line segments per branch
int size=60;
int max_write_len=60; // disregard any piece longer than this number
double redux=3.0; // how much to divide # of segments...lower # means finer 'res.' (more objects)
int min_len=1; // min # of line segments (spheres per branch)
int x,y; // display vector...object vector is derived from this also
//main
int main(int argc, char* argv[])
{
if(argc>1)process_args (argc, argv);
show_title();
get_inputs(); // get user input and check for 'validity'
outfile.open(filename,ios::out); // try to open disk file
if(! outfile) exit(1); // if disk access error,exit
if(display) init_display(v_mode); // try to set graphics display
write_header();
outfile.setf(ios::showpoint | ios::fixed);
//outfile.precision(6);
srand( (unsigned)time(NULL) ); // seed random number
x=512,y=384; // set origin of cluster
internal_scale = 0.1111; // default object size is
overall_scale = internal_scale*user_scale; // approx.10x10x10
size = segs;
bound_val = segs*0.5; // seems to be the best figure as far as rendering
// speed and bound accuracy are concerned
if(bound_val < 20) bound_val = 20; // don't want too many bounds
if(display) cerr << "TOP VIEW (XZ)\n";
cerr << "\nCalculating object... Press any key to abort\n\n";
start = clock(); // ****************** start timer
calc_cluster(size, x, y); // calculate plant and show progress
if(format != CTDS) write_union_end(); // write final nested bound if nec.
write_end();
outfile.close; // close disk file
finish = clock(); // ********************* stop timer
duration = (double)(finish - start) / CLOCKS_PER_SEC; // *** calc. time
if(display) {
cerr << "Press any key to continue...";
getch(); // wait for keypress
close_display(); // restore video to previous state
}
cout << endl << counter << " objects used to create " << filename;
cout << "\nCalculation time : "<<duration<< " seconds\n"; // show time
return(0);
}
/****************************** **************************
****************************** FUNCTION DEFINITIONS *************************
****************************** *************************/
void calc_bounds()
{
// compute min and max object vectors for current bounding box if needed
if(format != CTDS && nest_bounds){
min.x = __min(min.x,ray.x);
max.x = __max(max.x,ray.x);
min.y = __min(min.y,ray.y);
max.y = __max(max.y,ray.y);
min.z = __min(min.z,ray.z);
max.z = __max(max.z,ray.z);
}
gmin.x = __min(gmin.x,ray.x); // now globally
gmax.x = __max(gmax.x,ray.x);
gmin.y = __min(gmin.y,ray.y);
gmax.y = __max(gmax.y,ray.y);
gmin.z = __min(gmin.z,ray.z);
gmax.z = __max(gmax.z,ray.z);
}
void calc_branch(int size, double theta, double x, double y)
{
for(int j=0; j<size; j++)
{ // get rand. #, range 0 to denom
int randy = rand() % denom;
int chng = (randy<nume) ? -1 : 1; // left or right ??
theta = theta + chng * deltheta; // new angle
x = x + rad*sin(theta); // x and y
y = y + rad*cos(theta); // of next point
if (size <= max_write_len ){ // this decides whether or not
// to execute next statements
/*************************************************************/
if(size</*4*/(segs*0.0667)){ ray.y =j+(segs-3)+(segs/redux)+((segs/redux)/redux);
if(display) _setcolor(RED);
if(j==0) strcpy(texture,"flower_tex");
sphere_rad = basic_rad*0.85;
}
else if(size</*13*/(segs*0.22)){ ray.y =j+(segs-2)+(segs/redux);
if(display) _setcolor(YELLOW);
if(j==0) strcpy(texture,"sub_branch_tex");
sphere_rad = basic_rad*1.2;
}
else if(size</*40*/(segs*0.675)){ ray.y =j+(segs-1);
if(display) _setcolor(LIGHTGREEN);
if(j==0) strcpy(texture,"branch_tex");
sphere_rad = basic_rad*1.6;
}
else{ ray.y =j;
if(display) _setcolor(GREEN); //dk green
if(j==0) strcpy(texture,"stem_tex");
sphere_rad = basic_rad*2.0;
}
if(display){
if(! write_texture) _setcolor(GREEN); //no textures, right??
if (v_mode==1) _lineto(int(x*0.625),int(y*0.625));
else if(v_mode==2) _lineto(int(x*0.78125),int(y*0.78125));
else _lineto(int(x),int(y)); // draw line
}
ray.x = ((double)(x-512)*0.3)*overall_scale; // translate sizes to
ray.z = ((double)(y-384)*0.3)*overall_scale; // file sizes based on
ray.y = ray.y*overall_scale*ht_scale; // scaling info...
sphere_rad *= overall_scale;
if(counter==0){ //first time around only
prev_sphere_rad = sphere_rad;
max_radius = sphere_rad;
}
bound_radi = prev_sphere_rad; //current bound, radius to add to bound
if((format != CTDS) && // if it's POV or Polyray AND we're
(bound_count==bound_val /*&& nest_bounds*/ || //ready for a bound OR....
prev_sphere_rad != sphere_rad /*&& nest_bounds*/)// the sphere radius changed...
){ // so it's time for a new bound
write_union_end();
new_union();
if(nest_bounds){ // added IF
total_bounds++;
reset_bounds();
}
bound_count=1;
write_piece();
}
else {
bound_count++;
write_piece();
}
strcpy(prev_texture, texture);
prev_sphere_rad = sphere_rad;
prev_ray.x = ray.x;
prev_ray.y = ray.y;
prev_ray.z = ray.z;
calc_bounds();
counter++; // increment object count
}
/***************************************************/
}
if(size>min_len)
{ // if branch is long enough,
int newsize = size / redux; // make a new one, but smaller
calc_cluster(newsize,x,y); // than before.
}
}
void calc_cluster(int size, double x, double y)
{
if( kbhit()) end_run();
for(int i=0; i<number; i++) // for each branch
{
double theta = i * 2 * pi / number;
if(display){
if (v_mode==1) _moveto(int(x*0.625),int(y*0.625));
else if(v_mode==2) _moveto(int(x*0.78125),int(y*0.78125));
else _moveto(int(x),int(y)); // get set to draw next line
}
// make a branch
calc_branch(size, theta, x, y);
}
}
void close_display()
{
_setvideomode(_DEFAULTMODE); // restore video to previous state
}
void end_run()
{
if(format != CTDS) write_union_end();
write_end();
if(display) close_display();
cerr << "\n--RUN ABORTED--\n";
cout << counter << " objects used to create " << filename << endl;
exit(1);
}
void get_inputs()
{
double noise=1.0,pi_distort=1.0,sphere_mult=1.0,denom_mult=1.0;
cout<< "\n1\tPOV 2.0\n2\tPOV 1.0\n3\tPolyray\n4\tCTDS\n" << "Number for format? [1]: ";
cin.getline(buff,256); int choice = atoi(buff);
switch(choice){ // output format
case 2 : format = POV1; strcpy(format_name,"POV-Ray v1.0"); break;
case 3 : format = POLY; strcpy(format_name,"Polyray v1.6"); break;
case 4 : format = CTDS; strcpy(format_name,"CTDS"); break;
default: format = POV2; strcpy(format_name,"POV-Ray v2.0");
}
cout << "Ouput filename? [plant.inc]: ";
cin.getline(buff,256); strcpy(filename,buff);
if(format != CTDS){
cout << "Union name? [plant]: ";
cin.getline(buff,256); strcpy(union_name,buff); }
cout << "Number of branches (recursive) ? [4]:";
cin.getline(buff,256); number = atoi(buff);
cout << "Maximum # of spheres per branch ? [60]:";
cin.getline(buff,256); segs = atoi(buff); if(segs==0) segs = 60;
cout << "Minimum # of spheres per branch ? [1]:";
cin.getline(buff,256); min_len = atoi(buff);
if(extended) {
cout << "Disregard branches longer than ? [" <<segs<< "]:";
cin.getline(buff,256); max_write_len = atoi(buff);
cout << "Divide branches by ? [3.0]:";
cin.getline(buff,256); redux = atof(buff);
cout << "Radial distortion (PI * x) ? [1.0]:";
cin.getline(buff,256); pi_distort = atof(buff);
cout << "Branch rotation factor ? [1.0]:";
cin.getline(buff,256); denom_mult = atof(buff);
cout << "Overall noise factor ? [1.0]:";
cin.getline(buff,256); noise = atof(buff);
cout << "Height scaling ? [1.0]:";
cin.getline(buff,256); ht_scale = atof(buff);
cout << "Sphere scaling ? [1.0]:";
cin.getline(buff,256); sphere_mult = atof(buff); }
cout << "Global scaling ? [1.0]: ";
cin.getline(buff,256); user_scale = atof(buff);
// Set up default values if applicable
if(filename[0]=='\0') strcpy(filename,"plant.inc");
if(union_name[0]=='\0') strcpy(union_name,"plant");
if(min_len==0) min_len = 1;
if(denom_mult==0.0) denom_mult = 1.0; denom *= denom_mult;
if(number==0) number = 4; if(user_scale==0.0) user_scale = 1.0;
if(pi_distort==0.0) pi_distort = 1.0; pi *= pi_distort;
if(noise==0.0) noise = 1.0; deltheta *= noise;
/*if(segs==0) segs = 60;*/ if(max_write_len==0) max_write_len = segs;
if(redux==0.0) redux = 3.0;
if(ht_scale==0.0) ht_scale = 1.0;
if(sphere_mult==0.0) sphere_mult = 1.0; basic_rad *= sphere_mult;
if(min_len == segs && max_write_len < min_len){
// this would cause a math error
min_len--; // not what user wanted, but better than a stack underflow!!
}
}
void init_display(int v_mode)
{
switch(v_mode){
case 3 : if(!_setvideomode(_XRES16COLOR)) {cerr << "\nCouldn't set 1024x768 graphics mode... trying 800x600\n"; v_mode = 2;}
else break; /* 1024x768x16 */
case 2 : if(!_setvideomode(_SRES16COLOR)) {cerr << "\nCouldn't set 800x600 graphics mode... trying 640x480\n"; v_mode = 1;}
else break; /* 800x600x16 */
case 1 : if(!_setvideomode(_VRES16COLOR)) {cerr << "\nCouldn't set 640x480 graphics mode... continuing without display\n"; v_mode = 0;}
else break; /* 640x480x16 */
default : display = FALSE; /* no display */
}
}
void new_union()
{
switch(format){
case POV1 :
outfile << " object{\n union{\n"; break;
case POLY :
outfile << "+object{ \n"; break;
default : // POV2
outfile << " object{\n union{\n";
}
}
void process_args (int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
if (argv[i][0] == '-' || argv[i][0] == '/')
process_option (&argv[i][1]);
else
{usage(); exit(1);}
}
}
void process_option (char* s)
{
switch (toupper(s[0]))
{
case 'B': nest_bounds = TRUE; break; //def=FALSE, don't write bounds
case 'E': extended = TRUE; break;
case 'O': spheres_only = TRUE; break;
case 'P': set_precision(&s[1]); break;
case 'D': display = TRUE; v_mode = atoi(&s[1]); break; //def=no display
case 'S': bound_sum = TRUE; break; //def=FALSE, global bound report only
case 'T': write_texture = FALSE; break; //def=TRUE, write textures
case '?': usage(); exit(0);
case 'H': usage(); exit(0);
default : usage(); exit(1);
}
}
void reset_bounds()
{
min.x=FLT_MAX, min.y=FLT_MAX, min.z=FLT_MAX; //
max.x=FLT_MIN, max.y=FLT_MIN, max.z=FLT_MIN; // reset for new bounding box
}
void set_precision(char* prec)
{
outfile.precision((atoi(prec) > 2 ? atoi(prec) : 6 ));
}
void show_title()
{
cout << "\nPlant v"<< VERSION << ", Copyright (c) 1993 Rob Bryerton\n"
<< "Creates a data file of a plant-like object for the POV-Ray v1.0\n"
<< "and v2.0 and Polyray v1.6 raytracers, and the Connect The Dots\n"
<< "Smoother (CTDS).\n";
}
void write_header()
{
if(format==CTDS){ strcpy(com,";"); strcpy(union_name,"-----");}
outfile
<<com<<" FILE: "<<filename<<" UNION NAME: "<<union_name<<" FORMAT: "<<format_name<< endl
<<com<<" This data file created by PLANT.EXE v"<<VERSION<<" for the POV-Ray v1.0 and\n"
<<com<<" v2.0 and Polyray v1.6 raytracers, and the Connect The Dots Smoother (CTDS).\n"
<<com<<" ----------------SEE END OF LISTING FOR OBJECT EXTENTS----------------------\n\n";
switch(format) {
case POLY :
if(write_texture) {
outfile
<< "include \"..\\colors.inc\"\n"
<< "define stem_tex texture{ matte { color <0.0, 0.65, 0.0> } }\n"
<< "define branch_tex texture{ matte { color <0.0, 0.8, 0.0> } }\n"
<< "define sub_branch_tex texture{ matte { color yellow } }\n"
<< "define flower_tex texture{ matte { color red } }\n";
}
outfile
<< "\ndefine " << union_name << endl
<< "object{ \n object{\n";
break;
case POV1 :
outfile
<< "#declare stem_tex= texture{ diffuse 0.7 color green 0.65 } \n";
if(write_texture) {
outfile
<< "#declare branch_tex= texture{ diffuse 0.7 color green 0.8 } \n"
<< "#declare sub_branch_tex= texture{ diffuse 0.7 color Yellow } \n"
<< "#declare flower_tex= texture{ diffuse 0.7 color Red } \n";
}
outfile
<< "\n#declare "<<union_name<<" = composite{ \nobject{\n union{\n";
break;
default : // POV2
if(write_texture) {
outfile
<< "#declare stem_tex= texture{ pigment{color green 0.65} finish{diffuse 0.7} } \n"
<< "#declare branch_tex= texture{ pigment{color green 0.8} finish{diffuse 0.7} } \n"
<< "#declare sub_branch_tex= texture{ pigment{color Yellow} finish{diffuse 0.7} } \n"
<< "#declare flower_tex= texture{ pigment{color Red} finish{diffuse 0.7} } \n";
}
outfile
<< "\n#declare "<<union_name<<" = union{ \nobject{\n union{\n";
}
}
void write_piece()
{
switch(format){
case POLY :
if(counter !=0 && bound_count!=1) outfile << " +"; // for Polyray union
else outfile << " ";
if(counter==0 || bound_count == 1 || spheres_only) //
outfile << "object{ sphere < " <<ray.x<< ", " << ray.y << ", " <<ray.z<< " >, " << sphere_rad << " }\n";
else{
outfile << "object{ sphere < " <<ray.x<< ", " << ray.y << ", " <<ray.z<< " >, " << sphere_rad << " }\n";
outfile << " +object{ cone < " <<prev_ray.x<< ", " << prev_ray.y << ", " <<prev_ray.z<< " >, " << sphere_rad << ", < " <<ray.x<< ", " << ray.y << ", " <<ray.z<< " >, " << sphere_rad << " }\n";
counter++; bound_count++; //add another object to total
}
break;
case CTDS :
if(sphere_rad != prev_sphere_rad) outfile << endl; //insert blank line for CTDS for each new sphere size for /m option
outfile << ray.x<< " " << ray.y << " " <<ray.z<< " " << sphere_rad << endl;
break;
case POV1 :
outfile << " sphere{ < " <<ray.x<< " " << ray.y << " " <<ray.z<< " > " << sphere_rad << " }\n";
break;
default : // POV2
if(counter==0 || bound_count == 1 || spheres_only) //
outfile << " sphere{ < " <<ray.x<< ", " << ray.y << ", " <<ray.z<< " >, " << sphere_rad << " }\n";
else{
outfile << " sphere{ < " <<ray.x<< ", " << ray.y << ", " <<ray.z<< " >, " << sphere_rad << " }\n";
outfile << " cone{ < " <<prev_ray.x<< ", " << prev_ray.y << ", " <<prev_ray.z<< " >, " << sphere_rad << ", < " <<ray.x<< ", " << ray.y << ", " <<ray.z<< " >, " << sphere_rad << " }\n";
counter++; bound_count++; //add another object to total
} // else
} // switch
} // function
void write_union_end()
{
Vector cbmin,cbmax; // current bound min and max vectors
if(nest_bounds){
cbmax.x = max.x + bound_radi;
cbmin.x = min.x - bound_radi;
cbmax.y = max.y + bound_radi;
cbmin.y = min.y - bound_radi;
cbmax.z = max.z + bound_radi;
cbmin.z = min.z - bound_radi;
}
switch(format){
case POLY :
if(write_texture) outfile << " " << prev_texture << endl;
if(nest_bounds){
outfile << " bounding_box < " <<cbmin.x<<", "<<cbmin.y<<", "<<cbmin.z<<" >,<"
<<cbmax.x<<", "<<cbmax.y<<", "<<cbmax.z<<" >\n";
}
outfile << " }"; // close the current union
break;
case POV1 :
outfile << " }\n"; // close the current union
if(write_texture) outfile << " texture{ " << prev_texture << " }\n";
else outfile << " texture{ stem_tex }\n";
if(nest_bounds){ outfile << " bounded_by{ \n"
<< " box{ < " << cbmin.x<<" "<<cbmin.y<<" "<<cbmin.z << "> < "
<< cbmax.x<<" "<<cbmax.y<<" "<<cbmax.z << "> }\n }\n";
}
outfile << " }"; // close current object
break;
default : // POV2
outfile << " }\n";
if(write_texture) outfile << " texture{ " << prev_texture << " }\n";
if(nest_bounds){ outfile << " bounded_by{ \n"
<< " box{ < " << cbmin.x<<", "<<cbmin.y<<", "<<cbmin.z << ">, < "
<< cbmax.x<<", "<<cbmax.y<<", "<<cbmax.z << "> }\n }\n";
}
outfile << " }"; // close current object
}
if(bound_sum && nest_bounds){
outfile <<com<<" Current bounding box info...\n"
<<com<<" Min. x : " << cbmin.x << endl
<<com<<" y : " << cbmin.y << endl
<<com<<" z : " << cbmin.z << endl
<<com<<" Max. x : " << cbmax.x << endl
<<com<<" y : " << cbmax.y << endl
<<com<<" z : " << cbmax.z;
}
outfile << endl;
}
void write_end()
{
gmax.x += max_radius;
gmin.x -= max_radius;
gmax.y += max_radius;
gmin.y -= max_radius;
gmax.z += max_radius;
gmin.z -= max_radius;
if(nest_bounds){
switch(format){
case POLY :
outfile << " bounding_box < " <<gmin.x<<", "<<gmin.y<<", "<<gmin.z<<" >,<"
<<gmax.x<<", "<<gmax.y<<", "<<gmax.z<<" >\n";
break;
case POV1 :
outfile << " bounded_by{\n"
<< " box{ < " << gmin.x<<" "<<gmin.y<<" "<<gmin.z << "> < "
<< gmax.x<<" "<<gmax.y<<" "<<gmax.z << "> }\n }\n";
break;
default : //POV2
outfile << " bounded_by{\n"
<< " box{ < " << gmin.x<<", "<<gmin.y<<", "<<gmin.z << ">, < "
<< gmax.x<<", "<<gmax.y<<", "<<gmax.z << "> }\n }\n";
}
}
outfile << "}\n\n"; // close 'master' union
// this next block is output for ALL formats w/the appropriate comment char's
if(format != CTDS && nest_bounds){
outfile <<com<<" Bounds written...."<< total_bounds << endl;
}
outfile <<com<<" Objects written..."<< counter << endl << endl
<<com<<" Object extents are as follows...\n"
<<com<<" Min. x : " << gmin.x << endl
<<com<<" y : " << gmin.y << endl
<<com<<" z : " << gmin.z << endl
<<com<<" Max. x : " << gmax.x << endl
<<com<<" y : " << gmax.y << endl
<<com<<" z : " << gmax.z << endl;
}
void usage()
{
cerr << "\n Plant v" << VERSION << endl
<< "Usage: plant [options]\n"
<< "Options: -b Write bounds in POV or Polyray output.\n"
<< " -d# enables 2D (xz) display of plant... # is the graphics mode.\n"
<< " 0=no display 1=640x480 2=800x600 3=1024x768 \n"
<< " Default value is 0.\n"
<< " -e Gives you a menu with an extended choice of options.\n"
<< " -o Use spheres ONLY (no cones) in output file.\n"
<< " -p# where # is the precision of the output file numbers.\n"
<< " Range is 3 - 9 with a default precision of 6 decimal places.\n"
<< " -s Include all bounding stats in output file. (POV and Polyray)\n"
<< " -t Do NOT write textures in output file.\n"
<< "\nType PLANT with no parameters for the default menus and options.\n";
}